
import pygtk
pygtk.require('2.0')

import os

import gtk
import gobject
import xdg.BaseDirectory

from statusnotifications.notifier import Notifier

SERVICE_COLUMN = 0
NAME_COLUMN = 1
VERIFIED_COLUMN = 2

class ObservableSet(gobject.GObject):
    __gsignals__ = {'add': (gobject.SIGNAL_RUN_LAST,
                            gobject.TYPE_NONE,
                            (object,)),
                    'remove': (gobject.SIGNAL_RUN_LAST,
                               gobject.TYPE_NONE,
                               (object,))}
    def __init__(self, wrapped_set):
        gobject.GObject.__init__(self)
        self.objects = set(wrapped_set)

    def add(self, obj):
        self.objects.add(obj)
        self.emit('add', obj)

    def remove(self, obj):
        self.objects.remove(obj)
        self.emit('remove', obj)

    def __iter__(self):
        return iter(self.objects)

class ConfigWindow(gtk.Window):
    '''The main configuration window.'''

    def __init__(self, cfg, mgr):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        self.connect('destroy', lambda *args: gtk.main_quit())

        self.resize(400, 300)

        notebook = gtk.Notebook()
        self.add(notebook)

        notifier_cfg_tab = NotifierConfigTab(cfg, mgr)
        notebook.append_page(notifier_cfg_tab.body, notifier_cfg_tab.label)

        accounts_cfg_tab = AccountsConfigTab(cfg, mgr)
        notebook.append_page(accounts_cfg_tab.body, accounts_cfg_tab.label)

class Form(gtk.Table):
    def __init__(self):
        gtk.Table.__init__(self, 1, 1)

        self.__next_row = 0

    def int_value(self, desc, model, property_name, lower, upper, step_incr,
                  units=''):
        label = gtk.Label(desc)

        if units:
            input_widget = gtk.HBox()
            value_adj = gtk.Adjustment(value=model[property_name],
                                       lower=lower,
                                       upper=upper,
                                       step_incr=step_incr)
            def change(adj, model, property_name):
                '''The value was changed.'''
                model[property_name] = int(adj.get_value())

            value_adj.connect('value-changed', change, model, property_name)
            timeout_value = gtk.SpinButton(value_adj)

            input_widget.pack_start(timeout_value, expand=False)
            input_widget.pack_start(gtk.Label(units),
                                    expand=False)

        self._add_row(label, input_widget)

    def text_value(self, desc, model, property_name, max_length=0,
                   is_password=False):
        label = gtk.Label(desc)

        input_widget = gtk.Entry(max_length)
        input_widget.set_visibility(not is_password)
        input_widget.set_text(model[property_name])

        def change(entry, model, property_name):
            '''The value was changed.'''
            model[property_name] = entry.get_text()
        input_widget.connect('changed', change, model, property_name)

        self._add_row(label, input_widget)

    def list_value(self, desc, model, property_name, values):
        label = gtk.Label(desc)

        input_widget = gtk.combo_box_new_text()
        for value in values:
            input_widget.append_text(value)

        input_widget.set_active(values.index(model[property_name]))

        def change(combo, model, property_name):
            model[property_name] = combo.get_active_text()

        input_widget.connect('changed', change, model, property_name)

        self._add_row(label, input_widget)

    def button_action(self, desc, button_msg, action):
        label = gtk.Label(desc)

        input_widget = gtk.Button(label=button_msg)
        input_widget.connect('clicked', lambda button: action())

        self._add_row(label, input_widget)

    def _add_row(self, left_widget, right_widget):
        '''Add a row to the form.'''
        row = self.__next_row
        self.__next_row += 1
        self.resize(row + 1, 2)

        align_left = gtk.Alignment(0.0, 0.0, 0.0, 0.0)
        align_left.add(left_widget)
        align_left2 = gtk.Alignment(0.0, 0.0, 0.0, 0.0)
        align_left2.add(right_widget)
        self.attach(align_left, 0, 1, row, row + 1,
                    xoptions=gtk.EXPAND | gtk.FILL, yoptions=0)
        self.attach(align_left2, 1, 2, row, row + 1,
                    xoptions=gtk.FILL, yoptions=0)

class NotifierConfigTab(object):
    '''Tab containing the notifier configuration.'''

    def __init__(self, cfg, mgr):
        self.label = gtk.Label('Notifier')
        self.body = Form()

        self.body.int_value("Time between checks:",
                            cfg,
                            'sleep',
                            1,
                            60,
                            1,
                            units="minute(s)")

        self.body.int_value("Timeout for the notification:",
                            cfg,
                            'timeout',
                            1,
                            60,
                            1,
                            units="seconds(s)")

        self.body.list_value("Notifier:",
                             cfg,
                             'notifier',
#                              ('asd', 'dsa'))
                             list(Notifier.plugin_list()))


class AccountsConfigTab(gtk.Container):
    '''Tab containing the accounts configuration.'''

    def __init__(self, cfg, mgr):
        self.label = gtk.Label('Accounts')
        self.body = gtk.VBox()

        acc_list = AccountList(mgr, cfg, 'accounts')
        self.body.pack_start(acc_list)

        btn_box = gtk.HBox()
        self.body.pack_start(btn_box, expand=False)

        add_btn = gtk.Button(stock=gtk.STOCK_ADD)
        btn_box.pack_start(add_btn)
        add_btn.connect('clicked', lambda btn:
                                     AddAccountAction(btn.get_toplevel(),
                                                      cfg['accounts'],
                                                      cfg,
                                                      mgr))

        rem_btn = gtk.Button(stock=gtk.STOCK_REMOVE)
        btn_box.pack_start(rem_btn)
        rem_btn.connect('clicked', lambda btn: acc_list.remove_selected())

class AccountList(gtk.TreeView):
    def __init__(self, mgr, model, property_name):
        model['accounts'] = ObservableSet(model['accounts'])
        self.__model = model
        self.__property_name = property_name

        self.__list_model = gtk.ListStore(gtk.gdk.Pixbuf, str, bool)

        gtk.TreeView.__init__(self, self.__list_model)

        def verified_cell_data(column, cell, model, iterator):
            valid = model.get_value(iterator, VERIFIED_COLUMN)
            if valid:
                cell.set_property('stock-id', gtk.STOCK_APPLY)
            else:
                cell.set_property('stock-id', gtk.STOCK_CANCEL)

        self.insert_column_with_attributes(0,
                                           "Service",
                                           gtk.CellRendererPixbuf(),
                                           pixbuf=SERVICE_COLUMN
                                           )
        self.insert_column_with_attributes(1,
                                           "Name",
                                           gtk.CellRendererText(),
                                           text=NAME_COLUMN
                                           )
        self.insert_column_with_data_func(2,
                                          "Verified",
                                          gtk.CellRendererPixbuf(),
                                          verified_cell_data
                                          )

        for account in self.__model[self.__property_name]:
            self._append_account(mgr, account)

        def account_added(account_list, account, mgr):
            self._append_account(mgr, account)
        self.__model[self.__property_name].connect('add', account_added, mgr)

    def _append_account(self, mgr, account):
        try:
            prov = account.create_provider()
            signed_in = True
        except mgr.get_interface('StatusProviderError'):
            signed_in = False
        pixbuf = self.__get_plugin_pixbuf(account.provider.image_name)
        self.__list_model.append([pixbuf, account.name, signed_in])

    def __get_plugin_pixbuf(self, file_name):
        data_paths = xdg.BaseDirectory.load_data_paths('status-notifications')
        for directory in data_paths:
            full_path = os.path.join(directory, file_name)
            if os.path.exists(full_path):
                break

        return gtk.gdk.pixbuf_new_from_file_at_size(full_path, 32, 32)

    def remove_selected(self):
        list_model, iterator = self.get_selection().get_selected()
        selected_name = list_model.get_value(iterator, NAME_COLUMN)
        list_model.remove(iterator)

        print 'Removing', selected_name

        acc_to_remove = None
        for account in self.__model[self.__property_name]:
            if account.name == selected_name:
                acc_to_remove = account
                break

        self.__model[self.__property_name].remove(account)

class AddAccountAction(object):
    """Open the account creation window."""

    def __init__(self, parent_window, account_list, cfg, mgr):
        dialog = AddAccountDialog(parent_window, mgr)
        responce = dialog.run()
        if responce == gtk.RESPONSE_ACCEPT:
            account = dialog.impl.create_account(dialog.user_data)
            account_list.add(account)
        dialog.destroy()

class AddAccountDialog(gtk.Dialog):
    """Input the account data."""

    def __init__(self, parent, mgr):
        gtk.Dialog.__init__(self, "Add Account", parent,
                            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
                             gtk.STOCK_ADD, gtk.RESPONSE_ACCEPT))

        self.user_data = None
        self.impl = None

        plugin_index = []

        plugin_select = gtk.combo_box_new_text()
        for impl in mgr.get_implementations('StatusProvider'):
            plugin_select.append_text(impl.display_name)
            plugin_index.append(impl)
        self.vbox.pack_start(plugin_select)
        plugin_select.show_all()

        body = Form()
        body.show_all()
        self.vbox.pack_start(body)


        def load_plugin_form(combo, form, plugin_index):
            plugin_i = combo.get_active()
            self.impl = plugin_index[plugin_i]
            self.user_data = self.impl.load_create_form(form)
        plugin_select.connect('changed', load_plugin_form, body, plugin_index)


if __name__ == '__main__':
    from statusnotifications.config import Account
    from statusnotifications.statusprovider import StatusProvider
    class TestStatusProvider(StatusProvider):
        name = 'test'
        def __init__(self, user, passwd):
            raise ApiError(2,3)
    cfg = {'sleep': 5, 'timeout': 15, 'notifier': 'asd',
            'accounts':[Account('Test', 'test', 'testuser')]}
    wnd = ConfigWindow(cfg)
    wnd.show_all()

    gtk.main()

    print cfg
